﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.ServiceModel.Web;
using System.ServiceModel;
using System.Xml.Serialization;
using System.IO;
using Microsoft.Http;
using System.Xml.Linq;

namespace CoderJoe.WoW.Proxies.Armory
{
    public class WoWArmoryProxyBase
    {
        public const string BaseUrlPath = "http://www.wowarmory.com/";

        protected TResult ExecuteMethod<TResult>(MethodBase method, params object[] arguments)
        {
            return (TResult)ExecuteMethod(method, typeof(TResult), arguments);
        }

        protected void ExecuteMethod(MethodBase method, params object[] arguments)
        {
            ExecuteMethod(method, typeof(void), arguments);
        }

        protected object ExecuteMethod(MethodBase method, Type returnType, params object[] arguments)
        {
            WebGetAttribute attr = FindWebGetAttribute(method);
            Dictionary<string, string> parameters = ParseParameters(method, arguments);
            Uri uri = BuildUri(attr, parameters);

            using (HttpClient client = new HttpClient(uri))
            {
                client.DefaultHeaders.Add("User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0;)");
                using (Stream response = client.Get().EnsureStatusIsSuccessful().Content.ReadAsStream())
                {
                    if (returnType == typeof(void))
                    {
                        return null;
                    }
                    else if (returnType == typeof(XElement))
                    {
                        using (TextReader reader = new StreamReader(response))
                        {
                            return XElement.Load(reader);
                        }
                    }
                    else if (returnType == typeof(Stream))
                    {
                        MemoryStream result = new MemoryStream();
                        const int BufferLength = 2048;

                        byte[] buffer = new byte[BufferLength];
                        int amountRead = 0;
                        do
                        {
                            amountRead = response.Read(buffer, 0, BufferLength);
                            result.Write(buffer, 0, amountRead);
                        } while (amountRead > 0);

                        return result;
                    }

                    XmlSerializer serializer = new XmlSerializer(returnType);
                    return serializer.Deserialize(response);
                }
            }
        }

        private static WebGetAttribute FindWebGetAttribute(MethodBase method)
        {
            WebGetAttribute attr = (WebGetAttribute)Attribute.GetCustomAttribute(method, typeof(WebGetAttribute));
            if (attr == null)
            {
                Type[] possibleContracts = method.DeclaringType.GetInterfaces();
                foreach (Type t in possibleContracts)
                {
                    if (Attribute.IsDefined(t, typeof(ServiceContractAttribute)))
                    {
                        InterfaceMapping mapping = method.DeclaringType.GetInterfaceMap(t);
                        int methodIndex = Array.IndexOf(mapping.TargetMethods, method);
                        if (methodIndex >= 0)
                        {
                            attr = (WebGetAttribute)Attribute.GetCustomAttribute(mapping.InterfaceMethods[methodIndex], typeof(WebGetAttribute));
                            if (attr != null)
                                break;
                        }
                    }
                }
            }
            return attr;
        }

        private Dictionary<string, string> ParseParameters(MethodBase method, object[] arguments)
        {
            ParameterInfo[] parameters = method.GetParameters();
            Dictionary<string, string> result = new Dictionary<string, string>();

            for (int i = 0; i < parameters.Length; i++)
            {
                result.Add(parameters[i].Name, arguments[i].ToString());
            }

            return result;
        }

        private Uri BuildUri(WebGetAttribute attr, Dictionary<string, string> arguments)
        {
            UriTemplate template = new UriTemplate(attr.UriTemplate);
            Uri baseUri = new Uri(BaseUrlPath);

            return template.BindByName(baseUri, arguments);
        }
    }
}
